June 21st, 2023
How to Build a Super Simple HTTP proxy in C ?

Here's a step-by-step guide on creating a simple HTTP proxy server using C :

Step 1: Set up the Project Create a new directory for your project and navigate into it in your terminal or command prompt.

Step 2: Create a New C Source File Create a new C source file in your project directory. You can name it whatever you like. In this example, let's name it SimpleProxyServer.cpp.

Step 3: Implement the Proxy Server Open the SimpleProxyServer.cpp file in your preferred C IDE or text editor and paste the following code:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define MAX_BUFFER_SIZE 4096
#define PORT 9097

std::string extractTargetUrl(const std::string& request) {
    std::istringstream iss(request);
    std::string firstLine;
    std::getline(iss, firstLine);

    size_t start = firstLine.find(' ')   1;
    size_t end = firstLine.rfind(' ');
    return firstLine.substr(start, end - start);
}

void handleClientRequest(int clientSocket) {
    char buffer[MAX_BUFFER_SIZE];
    memset(buffer, 0, sizeof(buffer));

    // Read the client's request
    ssize_t bytesRead = read(clientSocket, buffer, sizeof(buffer));
    std::string request(buffer, bytesRead);

    // Extract the target URL from the request
    std::string targetUrl = extractTargetUrl(request);

    // Open a connection to the target server
    struct sockaddr_in targetAddr{};
    memset(&targetAddr, 0, sizeof(targetAddr));
    targetAddr.sin_family = AF_INET;
    targetAddr.sin_port = htons(80);

    struct hostent* host = gethostbyname(targetUrl.c_str());
    memcpy(&targetAddr.sin_addr, host->h_addr_list[0], host->h_length);

    int targetSocket = socket(AF_INET, SOCK_STREAM, 0);
    connect(targetSocket, (struct sockaddr*)&targetAddr, sizeof(targetAddr));

    // Forward the client's request to the target server
    write(targetSocket, request.c_str(), request.length());

    // Forward the target server's response to the client
    memset(buffer, 0, sizeof(buffer));
    while ((bytesRead = read(targetSocket, buffer, sizeof(buffer))) > 0) {
        write(clientSocket, buffer, bytesRead);
        memset(buffer, 0, sizeof(buffer));
    }

    // Close the sockets
    close(targetSocket);
    close(clientSocket);
}

int main() {
    int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (serverSocket == -1) {
        std::cerr << "Failed to create server socket." << std::endl;
        return 1;
    }

    struct sockaddr_in serverAddr{};
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(PORT);
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
        std::cerr << "Failed to bind server socket." << std::endl;
        return 1;
    }

    if (listen(serverSocket, 10) == -1) {
        std::cerr << "Failed to listen on server socket." << std::endl;
        return 1;
    }

    std::cout << "Now serving at :" << PORT << std::endl;

    while (true) {
        struct sockaddr_in clientAddr{};
        socklen_t clientAddrLen = sizeof(clientAddr);
        int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
        if (clientSocket == -1) {
            std::cerr << "Failed to accept client connection." << std::endl;
            continue;
        }

        pid_t pid = fork();
        if (pid == -1) {
            std::cerr << "Failed to fork." << std::endl;
            continue;
        }

        if (pid == 0) {
            // Child process
            close(serverSocket);
            handleClientRequest(clientSocket);
            return 0;
        }

        // Parent process
        close(clientSocket);
    }

    close(serverSocket);
    return 0;
}

Here's a breakdown of specific parts of the code to aid in understanding:

Server Setup:

int serverSocket = socket(AF_INET, SOCK_STREAM, 0);

This line creates a socket for the server to listen for incoming client connections.

struct sockaddr_in serverAddr{};
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);

if (bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
    // Error handling
}

These lines set up the server address and bind it to the server socket. It specifies the port number and sets the server to listen on all available network interfaces.

if (listen(serverSocket, 10) == -1) {
    // Error handling
}

This line starts the server listening for incoming client connections, with a maximum queue size of 10 pending connections.

Handling Client Requests:

void handleClientRequest(int clientSocket) {
    char buffer[MAX_BUFFER_SIZE];
    memset(buffer, 0, sizeof(buffer));

    // Read the client's request
    ssize_t bytesRead = read(clientSocket, buffer, sizeof(buffer));
    std::string request(buffer, bytesRead);

    // Extract the target URL from the request
    std::string targetUrl = extractTargetUrl(request);

    // ... (continued below)
}

This function is responsible for handling the client's request once a connection is accepted. It reads the request from the client socket and stores it in a buffer.

std::string extractTargetUrl(const std::string& request) {
    std::istringstream iss(request);
    std::string firstLine;
    std::getline(iss, firstLine);

    size_t start = firstLine.find(' ')   1;
    size_t end = firstLine.rfind(' ');
    return firstLine.substr(start, end - start);
}

This function extracts the target URL from the client's request. It searches for the URL in the first line of the request, following the HTTP method and before the HTTP version.

Forwarding Request and Response:

int targetSocket = socket(AF_INET, SOCK_STREAM, 0);
connect(targetSocket, (struct sockaddr*)&targetAddr, sizeof(targetAddr));

// Forward the client's request to the target server
write(targetSocket, request.c_str(), request.length());

// Forward the target server's response to the client
memset(buffer, 0, sizeof(buffer));
while ((bytesRead = read(targetSocket, buffer, sizeof(buffer))) > 0) {
    write(clientSocket, buffer, bytesRead);
    memset(buffer, 0, sizeof(buffer));
}

These lines establish a connection with the target server and forward the client's request to the target server by writing the request to the target socket.

The response from the target server is then read from the target socket in a loop and forwarded back to the client by writing it to the client socket.

Closing Connections:

close(targetSocket);
close(clientSocket);

These lines close the connections to the target server and the client once the request and response have been forwarded.

Step 4: Compile the Proxy Server Save the SimpleProxyServer.cpp file. In your terminal or command prompt, navigate to the project directory and compile the C source file by running the following command:

g   SimpleProxyServer.cpp -o SimpleProxyServer

Step 5: Run the Proxy Server After successful compilation, run the proxy server using the following command:

./SimpleProxyServer

You should see a message indicating that the server is running at http://localhost:9097.

Step 6: Test the Proxy Server To test the proxy server, open your web browser and configure it to use localhost as the proxy server and 9097 as the port. Then, visit any website using the browser, and you should see the response from the target website displayed.

Congratulations! You have successfully created a simple HTTP proxy server using C .

Please note that this implementation is a basic example and may not handle certain scenarios or provide the same level of functionality as a full-fledged proxy server. This is great as a learning exercise but it is easy to see that even the proxy server itself is prone to get blocked as it uses a single IP. In this scenario where you may want a proxy that handles thousands of fetches every day using a professional rotating proxy service to rotate IPs is almost a must.

Otherwise, you tend to get IP blocked a lot by automatic location, usage, and bot detection algorithms.

Our rotating proxy server Proxies API provides a simple API that can solve all IP Blocking problems instantly.

With millions of high speed rotating proxies located all over the world,With our automatic IP rotationWith our automatic User-Agent-String rotation (which simulates requests from different, valid web browsers and web browser versions)With our automatic CAPTCHA solving technology,

Hundreds of our customers have successfully solved the headache of IP blocks with a simple API.

The whole thing can be accessed by a simple API like below in any programming language.

In fact, you don't even have to take the pain of loading Puppeteer as we render Javascript behind the scenes and you can just get the data and parse it any language like Node, Puppeteer or PHP or using any framework like Scrapy or Nutch. In all these cases you can just call the URL with render support like so.

curl ""

We have a running offer of 1000 API calls completely free. Register and get your free API Key here.

Share this article:

Get our articles in your inbox

Dont miss our best tips/tricks/tutorials about Web Scraping
Only great content, we don’t share your email with third parties.
Icon